{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Perspectives on matrix multiplication\n",
    "\n",
    "\n",
    "Lots of you  seem to have learned how to multiply matrices ([matrix multiplication](https://en.wikipedia.org/wiki/Matrix_multiplication)) in high school.  \n",
    "We compute the product $C=AB$ of an $m \\times n$ matrix $A$ with an $n \\times p$ matrix $B$ to produce an $m \\times p$ matrix $C$.\n",
    "\n",
    "Did you ever wonder why \"matmul\" has such a fancy definition?\n",
    "\n",
    "When we add matrices we add elements.  Why coudn't matmul be just as easy?\n",
    "\n",
    "## Compare Elementwise Multiply\n",
    "\n",
    "Of course the elementwise multiply is doable but never seems to be quite as important:\n",
    "\n",
    "(I'll bet your high school teacher never mentioned elementwise multiply!)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A .* B = [7 4; 9 8]\n",
      "A * B = [13 6; 33 14]\n"
     ]
    }
   ],
   "source": [
    "A=[1 2\n",
    "   3 4]\n",
    "B=[7 2\n",
    "   3 2]\n",
    "@show(A .* B)    # Elementwise times is the \"dot star\"\n",
    "@show(A * B);    # Matmul is just the \"star\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For square n x n matrices, elementwise multiply requires $n^2$ operations, while matmul requires about $2n^3$. (Think $n^2$ dot products, each requiring $n$ mults and almost $n$ adds.)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Raising the Abstraction\n",
    "\n",
    "Why is matmul defined this way?  We will find out later in the course when we begin to understand that a matrix represents a linear transformation, and matmul is the natural representation of the composition of transformations.  It is only then you can understand the true nature of matrix multiplication.  (Bet your high school teacher never told you that!)\n",
    "\n",
    "One of our goals in 18.06 is to sometimes stop thinking of matrices as arrays of numbers, and more as holistic objects.\n",
    "\n",
    "Abstractly, the rules for matrix multiplication are determined once you define how to multiply matrices by vectors $Ax$, the central [linear operation](https://en.wikipedia.org/wiki/Linear_map) of 18.06, by requiring that multiplication be [associative](https://en.wikipedia.org/wiki/Associative_property).  That is, we require:\n",
    "$$\n",
    "A(Bx)=(AB)x\n",
    "$$\n",
    "for all matrices $A$ and $B$ and all vectors $x$.  The expression $A(Bx)$ involves only matrix × vector (computing $y=Bx$ then $Ay$), and requiring this to equal $(AB)x$ actually uniquely defines the matrix–matrix product $AB$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perspective 1 (high school!): rows × columns\n",
    "\n",
    "The  most familar definition is that you take **dot products of rows of A with columns of B** to get the product $C$.  For example:\n",
    "$$\n",
    "\\begin{pmatrix}\n",
    " -14 &   5 & 10 \\\\\n",
    "  \\color{red}{-5} & -20 & 10 \\\\\n",
    "  -6 &  10 &  6\n",
    "\\end{pmatrix} =\n",
    "\\begin{pmatrix}\n",
    " 2 & -1 & 5 \\\\\n",
    "  \\color{red}{3} &  \\color{red}{4} & \\color{red}{4} \\\\\n",
    " -4 & -2 & 0\n",
    "\\end{pmatrix}\n",
    "\\begin{pmatrix}\n",
    "\\color{red}{1}  & 0 & -2 \\\\\n",
    "  \\color{red}{1} & -5 &  1 \\\\\n",
    " \\color{red}{-3} &  0 &  3\n",
    "\\end{pmatrix}\n",
    "$$\n",
    "where we have highlighted the entry $\\color{red}{-5 = 3 \\times 1 + 4 \\times 1 + 4 \\times -3}$ (second row of $A$ ⋅ first column of $B$).\n",
    "\n",
    "This can be written out as the formula\n",
    "$$\n",
    "c_{ij} = \\sum_{k=1}^n a_{ik} b_{kj}\n",
    "$$\n",
    "in terms of the entries of the matrices, e.g. $c_{ij}$ is the entry in row $i$, column $j$ of $C$, assuming $A$ has $n$ columns and $B$ has $n$ rows.\n",
    "\n",
    "Essentially all matrix multiplications in practice are done with a version of this formula — at least, with the same operations, but often the *order* in which you multiply/add individual numbers is re-arranged.\n",
    "\n",
    "**In this notebook, we will explore several ways to *think* about these operations by re-arranging their order.**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = [ 2  -1  5\n",
    "      3   4  4\n",
    "     -4  -2  0]\n",
    "B = [ 1   0  -2\n",
    "      1  -5   1\n",
    "     -3   0   3]\n",
    "C = A * B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because matrix multiplication is generally [not commutative](https://en.wikipedia.org/wiki/Commutative_property), $AB$ and $BA$ give *different* matrices:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  10    3    5\n",
       " -17  -23  -15\n",
       " -18   -3  -15"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B*A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -24   2   5\n",
       "  12   3  25\n",
       "  12  13  21"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A*B - B*A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Though sometimes it can happen to be commutative."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Float64,2}:\n",
       " -138.0  -89.0  -147.0\n",
       " -101.0  -24.0    92.0\n",
       "   44.0   46.0  -132.0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A*(A^2 + 2*A + inv(A)*10) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Float64,2}:\n",
       " -138.0  -89.0  -147.0\n",
       " -101.0  -24.0    92.0\n",
       "   44.0   46.0  -132.0"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(A^2 + 2*A + inv(A)*10)  * A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we want, we can compute the individual dot products in Julia too.   For example, let's compute $c_{2,1} = -5$ (the 2nd row and first column of $C$, or `C[2,1]` in Julia) by taking the dot product of the second row of $A$ with the first column of $B$.\n",
    "\n",
    "To extract rows and columns of a matrix, Julia supports a syntax for \"array slicing\" pioneered by APL.  The second row of $A$ is `A[2,:]`, and the first column of `B` is `B[:,1]`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  2  -1  5\n",
       "  3   4  4\n",
       " -4  -2  0"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[2,3] # 2nd row, 3rd col"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       " 3\n",
       " 4\n",
       " 4"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[2,:] # 2nd row of A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  1   0  -2\n",
       "  1  -5   1\n",
       " -3   0   3"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       "  1\n",
       "  1\n",
       " -3"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B[:,1] # 1st column of B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can compute $c_{2,1}$ by their dot product via the `dot` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-5"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dot(A[2,:], B[:,1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also write the dot product with the $x \\dot y$ notation:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-5"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[2,:] ⋅ B[:,1]   # type \\cdot + tab"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "α = A[2,3] # \\alpha + tab"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This matches $c_{2,1}$ from above, or `C[2,1]` in Julia:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-5"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C[2,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-5"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[2,:]' * B[:,1]  # yet another way to get a dot product"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### The summation $$c_{ij} = \\sum_{k=1}^n a_{ik} b_{kj}$$ directly in a triple loop code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "matmul_ijk (generic function with 1 method)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "function matmul_ijk(A,B)\n",
    "   m,n = size(A)\n",
    "   n2,p = size(B)\n",
    "   if n≠n2 error(\"No good, n=$n ≠ n2=$(n2)\") end\n",
    "   \n",
    "   C = fill(0,m,p) # m x p \"zeros\" matrix\n",
    "    \n",
    "   for i=1:m, j=1:p, k=1:n\n",
    "      C[i,j] += A[i,k]*B[k,j]   # shorthand for C[i,j] = C[i,j] + A[i,k]*B[k,j] \n",
    "   end\n",
    "    \n",
    "   return C  \n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "matmul_ijk(A,B)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perspective 2: matrix × columns\n",
    "\n",
    "$AB$ can be viewed as multiplying $A$ on the *left* by each *column* of $B$.\n",
    "\n",
    "For example, let's multiply $A$ by the first column of $B$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A*B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       "  1\n",
       "  1\n",
       " -3"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B[:,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       " -14\n",
       "  -5\n",
       "  -6"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * B[:,1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "    <script class='js-collapse-script'>\n",
       "        var curMatch =\n",
       "            window.location.href\n",
       "            .match(/(.*?)\\/notebooks\\/.*\\.ipynb/);\n",
       "\n",
       "        curMatch = curMatch ||\n",
       "            window.location.href\n",
       "            .match(/(.*?)\\/apps\\/.*\\.ipynb/);\n",
       "\n",
       "        if ( curMatch ) {\n",
       "            $('head').append('<base href=\"' + curMatch[1] + '/\">');\n",
       "        }\n",
       "    </script>\n"
      ],
      "text/plain": [
       "HTML{String}(\"    <script class='js-collapse-script'>\\n        var curMatch =\\n            window.location.href\\n            .match(/(.*?)\\\\/notebooks\\\\/.*\\\\.ipynb/);\\n\\n        curMatch = curMatch ||\\n            window.location.href\\n            .match(/(.*?)\\\\/apps\\\\/.*\\\\.ipynb/);\\n\\n        if ( curMatch ) {\\n            \\$('head').append('<base href=\\\"' + curMatch[1] + '/\\\">');\\n        }\\n    </script>\\n\")"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<script class='js-collapse-script' src='/assetserver/2788aaff54f3972bd10a224d42612bcbc5553044-assets/webio/dist/bundle.js'></script>"
      ],
      "text/plain": [
       "HTML{String}(\"<script class='js-collapse-script' src='/assetserver/2788aaff54f3972bd10a224d42612bcbc5553044-assets/webio/dist/bundle.js'></script>\")"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<script class='js-collapse-script' src='/assetserver/2788aaff54f3972bd10a224d42612bcbc5553044-assets/providers/ijulia_setup.js'></script>"
      ],
      "text/plain": [
       "HTML{String}(\"<script class='js-collapse-script' src='/assetserver/2788aaff54f3972bd10a224d42612bcbc5553044-assets/providers/ijulia_setup.js'></script>\")"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "  <script class='js-collapse-script'>\n",
       "    $('.js-collapse-script').parent('.output_subarea').css('padding', '0');\n",
       "  </script>\n"
      ],
      "text/plain": [
       "HTML{String}(\"  <script class='js-collapse-script'>\\n    \\$('.js-collapse-script').parent('.output_subarea').css('padding', '0');\\n  </script>\\n\")"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "using Interact"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div class='tex2jax_ignore interactbulma'>\n",
       "<div class='display:none'></div><unsafe-script style='display:none'>\n",
       "WebIO.mount(this.previousSibling,{&quot;props&quot;:{},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;className&quot;:&quot;field&quot;},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{},&quot;nodeType&quot;:&quot;Scope&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;imports&quot;:{&quot;data&quot;:[{&quot;name&quot;:&quot;knockout&quot;,&quot;type&quot;:&quot;js&quot;,&quot;url&quot;:&quot;/assetserver/d6df0bcbf61025c952fa7d6260c0502952fc6253-knockout.js&quot;},{&quot;name&quot;:&quot;knockout_punches&quot;,&quot;type&quot;:&quot;js&quot;,&quot;url&quot;:&quot;/assetserver/76b9e9b6191c21207f614aefd21121e841930334-knockout_punches.js&quot;},{&quot;name&quot;:null,&quot;type&quot;:&quot;js&quot;,&quot;url&quot;:&quot;/assetserver/aee18abf5b39fa7dbdb49e13ea37aa8617a93fca-all.js&quot;},{&quot;name&quot;:null,&quot;type&quot;:&quot;css&quot;,&quot;url&quot;:&quot;/assetserver/b8ef71fc8f8c937f705d60a54c85dd170cf7e73f-style.css&quot;},{&quot;name&quot;:null,&quot;type&quot;:&quot;css&quot;,&quot;url&quot;:&quot;/assetserver/342c1e68f950ec6d9432ff00342c66dae6d08a65-main.css&quot;}],&quot;type&quot;:&quot;async_block&quot;},&quot;id&quot;:&quot;knockout-component-16694083-7eb6-4348-b89a-28d470b67f09&quot;,&quot;handlers&quot;:{&quot;_promises&quot;:{&quot;importsLoaded&quot;:[function (ko, koPunches) {\n",
       "    ko.punches.enableAll();\n",
       "    ko.bindingHandlers.numericValue = {\n",
       "        init : function(element, valueAccessor, allBindings, data, context) {\n",
       "            var stringified = ko.observable(ko.unwrap(valueAccessor()));\n",
       "            stringified.subscribe(function(value) {\n",
       "                var val = parseFloat(value);\n",
       "                if (!isNaN(val)) {\n",
       "                    valueAccessor()(val);\n",
       "                }\n",
       "            })\n",
       "            valueAccessor().subscribe(function(value) {\n",
       "                var str = JSON.stringify(value);\n",
       "                if ((str == &quot;0&quot;) &amp;&amp; ([&quot;-0&quot;, &quot;-0.&quot;].indexOf(stringified()) &gt;= 0))\n",
       "                     return;\n",
       "                 if ([&quot;null&quot;, &quot;&quot;].indexOf(str) &gt;= 0)\n",
       "                     return;\n",
       "                stringified(str);\n",
       "            })\n",
       "            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get(&#39;valueUpdate&#39;)}, context);\n",
       "        }\n",
       "    };\n",
       "    var json_data = JSON.parse(&quot;{\\&quot;changes\\&quot;:0,\\&quot;value\\&quot;:2}&quot;);\n",
       "    var self = this;\n",
       "    function AppViewModel() {\n",
       "        for (var key in json_data) {\n",
       "            var el = json_data[key];\n",
       "            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\n",
       "        }\n",
       "        \n",
       "        [this[&quot;displayedvalue&quot;]=ko.computed(function () {return this.value();},this)]\n",
       "        [this[&quot;changes&quot;].subscribe((function (val){!(this.valueFromJulia[&quot;changes&quot;]) ? (WebIO.setval({&quot;name&quot;:&quot;changes&quot;,&quot;scope&quot;:&quot;knockout-component-16694083-7eb6-4348-b89a-28d470b67f09&quot;,&quot;id&quot;:&quot;ob_02&quot;,&quot;type&quot;:&quot;observable&quot;},val)) : undefined; return this.valueFromJulia[&quot;changes&quot;]=false}),self),this[&quot;value&quot;].subscribe((function (val){!(this.valueFromJulia[&quot;value&quot;]) ? (WebIO.setval({&quot;name&quot;:&quot;value&quot;,&quot;scope&quot;:&quot;knockout-component-16694083-7eb6-4348-b89a-28d470b67f09&quot;,&quot;id&quot;:&quot;ob_01&quot;,&quot;type&quot;:&quot;observable&quot;},val)) : undefined; return this.valueFromJulia[&quot;value&quot;]=false}),self)]\n",
       "        \n",
       "    }\n",
       "    self.model = new AppViewModel();\n",
       "    self.valueFromJulia = {};\n",
       "    for (var key in json_data) {\n",
       "        self.valueFromJulia[key] = false;\n",
       "    }\n",
       "    ko.applyBindings(self.model, self.dom);\n",
       "}\n",
       "]},&quot;changes&quot;:[(function (val){return (val!=this.model[&quot;changes&quot;]()) ? (this.valueFromJulia[&quot;changes&quot;]=true, this.model[&quot;changes&quot;](val)) : undefined})],&quot;value&quot;:[(function (val){return (val!=this.model[&quot;value&quot;]()) ? (this.valueFromJulia[&quot;value&quot;]=true, this.model[&quot;value&quot;](val)) : undefined})]},&quot;systemjs_options&quot;:null,&quot;observables&quot;:{&quot;changes&quot;:{&quot;sync&quot;:false,&quot;id&quot;:&quot;ob_02&quot;,&quot;value&quot;:0},&quot;value&quot;:{&quot;sync&quot;:true,&quot;id&quot;:&quot;ob_01&quot;,&quot;value&quot;:2}}},&quot;children&quot;:[{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;display:flex; justify-content:center; align-items:center;&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;text-align:right;width:18%&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;className&quot;:&quot;interact &quot;,&quot;style&quot;:{&quot;padding&quot;:&quot;5px 10px 0px 10px&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;label&quot;},&quot;children&quot;:[&quot;j&quot;]}]},{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;flex-grow:1; margin: 0 2%&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;max&quot;:3,&quot;min&quot;:1,&quot;attributes&quot;:{&quot;type&quot;:&quot;range&quot;,&quot;data-bind&quot;:&quot;numericValue: value, valueUpdate: &#39;input&#39;, event: {change : function () {this.changes(this.changes()+1)}}&quot;},&quot;step&quot;:1,&quot;className&quot;:&quot;slider slider is-fullwidth&quot;,&quot;style&quot;:{}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;input&quot;},&quot;children&quot;:[]}]},{&quot;props&quot;:{&quot;attributes&quot;:{&quot;style&quot;:&quot;width:18%&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[{&quot;props&quot;:{&quot;attributes&quot;:{&quot;data-bind&quot;:&quot;text: displayedvalue&quot;}},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;p&quot;},&quot;children&quot;:[]}]}]}]}]},{&quot;props&quot;:{},&quot;nodeType&quot;:&quot;Scope&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;imports&quot;:{&quot;data&quot;:[],&quot;type&quot;:&quot;async_block&quot;},&quot;id&quot;:&quot;scope-cf0ad405-f31b-41c5-8da2-3fac2f9d21a5&quot;,&quot;handlers&quot;:{&quot;obs-output&quot;:[function (updated_htmlstr) {\n",
       "    var el = this.dom.querySelector(&quot;#out&quot;);\n",
       "    WebIO.propUtils.setInnerHtml(el, updated_htmlstr);\n",
       "}]},&quot;systemjs_options&quot;:null,&quot;observables&quot;:{&quot;obs-output&quot;:{&quot;sync&quot;:false,&quot;id&quot;:&quot;ob_06&quot;,&quot;value&quot;:&quot;&lt;div class=&#39;display:none&#39;&gt;&lt;/div&gt;&lt;unsafe-script style=&#39;display:none&#39;&gt;\\nWebIO.mount(this.previousSibling,{&amp;quot;props&amp;quot;:{&amp;quot;attributes&amp;quot;:{&amp;quot;style&amp;quot;:&amp;quot;display:flex; justify-content:center; align-items:center;&amp;quot;}},&amp;quot;nodeType&amp;quot;:&amp;quot;DOM&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;node&amp;quot;,&amp;quot;instanceArgs&amp;quot;:{&amp;quot;namespace&amp;quot;:&amp;quot;html&amp;quot;,&amp;quot;tag&amp;quot;:&amp;quot;div&amp;quot;},&amp;quot;children&amp;quot;:[{&amp;quot;props&amp;quot;:{&amp;quot;setInnerHtml&amp;quot;:&amp;quot;&amp;lt;pre&amp;gt;3-element Array{Int64,1}:\\\\n   5\\\\n -20\\\\n  10&amp;lt;/pre&amp;gt;&amp;quot;},&amp;quot;nodeType&amp;quot;:&amp;quot;DOM&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;node&amp;quot;,&amp;quot;instanceArgs&amp;quot;:{&amp;quot;namespace&amp;quot;:&amp;quot;html&amp;quot;,&amp;quot;tag&amp;quot;:&amp;quot;div&amp;quot;},&amp;quot;children&amp;quot;:[]}]})&lt;/unsafe-script&gt;&quot;}}},&quot;children&quot;:[{&quot;props&quot;:{&quot;id&quot;:&quot;out&quot;,&quot;setInnerHtml&quot;:&quot;&lt;div class=&#39;display:none&#39;&gt;&lt;/div&gt;&lt;unsafe-script style=&#39;display:none&#39;&gt;\\nWebIO.mount(this.previousSibling,{&amp;quot;props&amp;quot;:{&amp;quot;attributes&amp;quot;:{&amp;quot;style&amp;quot;:&amp;quot;display:flex; justify-content:center; align-items:center;&amp;quot;}},&amp;quot;nodeType&amp;quot;:&amp;quot;DOM&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;node&amp;quot;,&amp;quot;instanceArgs&amp;quot;:{&amp;quot;namespace&amp;quot;:&amp;quot;html&amp;quot;,&amp;quot;tag&amp;quot;:&amp;quot;div&amp;quot;},&amp;quot;children&amp;quot;:[{&amp;quot;props&amp;quot;:{&amp;quot;setInnerHtml&amp;quot;:&amp;quot;&amp;lt;pre&amp;gt;3-element Array{Int64,1}:\\\\n   5\\\\n -20\\\\n  10&amp;lt;/pre&amp;gt;&amp;quot;},&amp;quot;nodeType&amp;quot;:&amp;quot;DOM&amp;quot;,&amp;quot;type&amp;quot;:&amp;quot;node&amp;quot;,&amp;quot;instanceArgs&amp;quot;:{&amp;quot;namespace&amp;quot;:&amp;quot;html&amp;quot;,&amp;quot;tag&amp;quot;:&amp;quot;div&amp;quot;},&amp;quot;children&amp;quot;:[]}]})&lt;/unsafe-script&gt;&quot;},&quot;nodeType&quot;:&quot;DOM&quot;,&quot;type&quot;:&quot;node&quot;,&quot;instanceArgs&quot;:{&quot;namespace&quot;:&quot;html&quot;,&quot;tag&quot;:&quot;div&quot;},&quot;children&quot;:[]}]}]})</unsafe-script>\n",
       "</div>"
      ],
      "text/plain": [
       "(div\n",
       "  Widgets.Widget{:slider}(DataStructures.OrderedDict{Symbol,Any}(:changes=>Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(#= circular reference @-7 =#), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37)), Observables.g]), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(#= circular reference @-8 =#), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37)), Observables.g]), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.#37))]),:value=>Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-7 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-8 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.#37)), Observables.g])), Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-7 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-8 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.#37)), Observables.g]), Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-7 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.SyncCallback(WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(#= circular reference @-8 =#), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), WebIO.#37)), Observables.g]), WebIO.Scope(\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :label), Any[\"j\"], Dict{Symbol,Any}(Pair{Symbol,Any}(:className, \"interact \"),Pair{Symbol,Any}(:style, Dict{Any,Any}(Pair{Any,Any}(:padding, \"5px 10px 0px 10px\")))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"text-align:right;width:18%\"))), 2), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :input), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:max, 3),Pair{Symbol,Any}(:min, 1),Pair{Symbol,Any}(:attributes, Dict{Any,Any}(Pair{Any,Any}(:type, \"range\"),Pair{Any,Any}(Symbol(\"data-bind\"), \"numericValue: value, valueUpdate: 'input', event: {change : function () {this.changes(this.changes()+1)}}\"))),Pair{Symbol,Any}(:step, 1),Pair{Symbol,Any}(:className, \"slider slider is-fullwidth\"),Pair{Symbol,Any}(:style, Dict{Any,Any}())), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"flex-grow:1; margin: 0 2%\"))), 1), WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :p), Any[], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"data-bind\"=>\"text: displayedvalue\"))), 0)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"width:18%\"))), 1)], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 7), Dict{String,Tuple{Observables.Observable,Union{Bool, Void}}}(Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"changes\", (Observables.Observable{Int64}(\"ob_02\", 0, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37))]), nothing)),Pair{String,Tuple{Observables.Observable,Union{Bool, Void}}}(\"value\", (Observables.Observable{Int64}(\"ob_01\", 2, Any[WebIO.SyncCallback(WebIO.Scope(#= circular reference @-7 =#), WebIO.SyncCallback(WebIO.Scope(#= circular reference @-8 =#), WebIO.#37)), Observables.g]), nothing))), Set{String}(), nothing, Any[\"knockout\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout.js\", \"knockout_punches\"=>\"/Users/stevenj/.julia/v0.6/Knockout/src/../assets/knockout_punches.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/all.js\", \"/Users/stevenj/.julia/v0.6/InteractBase/src/../assets/style.css\", \"/Users/stevenj/.julia/v0.6/InteractBulma/src/../assets/main.css\"], Dict{Any,Any}(Pair{Any,Any}(\"_promises\", Dict{Any,Any}(Pair{Any,Any}(\"importsLoaded\", Any[WebIO.JSString(\"function (ko, koPunches) {\\n    ko.punches.enableAll();\\n    ko.bindingHandlers.numericValue = {\\n        init : function(element, valueAccessor, allBindings, data, context) {\\n            var stringified = ko.observable(ko.unwrap(valueAccessor()));\\n            stringified.subscribe(function(value) {\\n                var val = parseFloat(value);\\n                if (!isNaN(val)) {\\n                    valueAccessor()(val);\\n                }\\n            })\\n            valueAccessor().subscribe(function(value) {\\n                var str = JSON.stringify(value);\\n                if ((str == \\\"0\\\") && ([\\\"-0\\\", \\\"-0.\\\"].indexOf(stringified()) >= 0))\\n                     return;\\n                 if ([\\\"null\\\", \\\"\\\"].indexOf(str) >= 0)\\n                     return;\\n                stringified(str);\\n            })\\n            ko.applyBindingsToNode(element, { value: stringified, valueUpdate: allBindings.get('valueUpdate')}, context);\\n        }\\n    };\\n    var json_data = JSON.parse(\\\"{\\\\\\\"changes\\\\\\\":0,\\\\\\\"value\\\\\\\":2}\\\");\\n    var self = this;\\n    function AppViewModel() {\\n        for (var key in json_data) {\\n            var el = json_data[key];\\n            this[key] = Array.isArray(el) ? ko.observableArray(el) : ko.observable(el);\\n        }\\n        \\n        [this[\\\"displayedvalue\\\"]=ko.computed(function () {return this.value();},this)]\\n        [this[\\\"changes\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"changes\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"changes\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_02\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"changes\\\"]=false}),self),this[\\\"value\\\"].subscribe((function (val){!(this.valueFromJulia[\\\"value\\\"]) ? (WebIO.setval({\\\"name\\\":\\\"value\\\",\\\"scope\\\":\\\"knockout-component-16694083-7eb6-4348-b89a-28d470b67f09\\\",\\\"id\\\":\\\"ob_01\\\",\\\"type\\\":\\\"observable\\\"},val)) : undefined; return this.valueFromJulia[\\\"value\\\"]=false}),self)]\\n        \\n    }\\n    self.model = new AppViewModel();\\n    self.valueFromJulia = {};\\n    for (var key in json_data) {\\n        self.valueFromJulia[key] = false;\\n    }\\n    ko.applyBindings(self.model, self.dom);\\n}\\n\")]))),Pair{Any,Any}(\"changes\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"changes\\\"]()) ? (this.valueFromJulia[\\\"changes\\\"]=true, this.model[\\\"changes\\\"](val)) : undefined})\")]),Pair{Any,Any}(\"value\", Any[WebIO.JSString(\"(function (val){return (val!=this.model[\\\"value\\\"]()) ? (this.valueFromJulia[\\\"value\\\"]=true, this.model[\\\"value\\\"](val)) : undefined})\")])), WebIO.ConnectionPool(Channel{Any}(sz_max:9223372036854775807,sz_curr:3), Set{WebIO.AbstractConnection}(), Channel{WebIO.AbstractConnection}(sz_max:32,sz_curr:0))), Widgets.#4, Base.#55)\n",
       "  Observables.Observable{Any}(\"ob_05\", WebIO.Node{WebIO.DOM}(WebIO.DOM(:html, :div), Any[[5, -20, 10]], Dict{Symbol,Any}(Pair{Symbol,Any}(:attributes, Dict(\"style\"=>\"display:flex; justify-content:center; align-items:center;\"))), 1), Any[]))"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "@manipulate for j=1:3\n",
    "    A * B[:,j]\n",
    "end"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is the first column of $C$!  If we do this to *all* the columns of $B$, we get $C$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ A*B[:,1]  A*B[:,2]  A*B[:,3] ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A*B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Equivalently, each column of $B$ specifies a [linear combination](https://en.wikipedia.org/wiki/Linear_combination) of *columns* of $A$ to produce the columns of $C$.   So, **if you want to rearrange the *columns* of a matrix, multiply it by another matrix on the *right***.\n",
    "\n",
    "For example, let's do the transformation that *flips the sign of the first column of $A$* and *swaps the second and third columns*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  2  -1  5\n",
       "  3   4  4\n",
       " -4  -2  0"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -2  5  -1\n",
       " -3  4   4\n",
       "  4  0  -2"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * [ -1  0  0\n",
    "       0  0  1\n",
    "       0  1  0  ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As another example, let's swap the first two columns:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -1   2  5\n",
       "  4   3  4\n",
       " -2  -4  0"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * [ 0 1 0\n",
    "      1 0 0\n",
    "      0 0 1 ]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  2  -1  5\n",
       "  3   4  4\n",
       " -4  -2  0"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A lot of students are perplexed.  They wonder how it could be legal to reorder in this way. \n",
    "It might take working through a few examples by hand to realize that from the perspective\n",
    "of C[i,j], the same sum is accumulated in the same order, but the order in which the different elements of C finish may vary. This little Julia demo may help with this understanding."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perspective 3: rows × matrix\n",
    "\n",
    "$AB$ can be viewed as multiplying each *row* of $A$ by the matrix $B$ on the *right*.  Multiplying a [row vector](https://en.wikipedia.org/wiki/Row_and_column_vectors) by a matrix on the right produces another row vector.\n",
    "\n",
    "For example, here is the first row of $A$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3-element Array{Int64,1}:\n",
       "  2\n",
       " -1\n",
       "  5"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[1,:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Whoops, slicing a matrix in Julia produces a 1d array, which is interpreted as a column vector, no matter how you slice it.  We can't multiply a column vector by a matrix $B$ on the *right* — that operation is not defined in linear algebra (the dimensions don't match up).  Julia will give an error if we try it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  2  -1  5\n",
       "  3   4  4\n",
       " -4  -2  0"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "ename": "LoadError",
     "evalue": "\u001b[91mDimensionMismatch(\"matrix A has dimensions (3,1), matrix B has dimensions (3,3)\")\u001b[39m",
     "output_type": "error",
     "traceback": [
      "\u001b[91mDimensionMismatch(\"matrix A has dimensions (3,1), matrix B has dimensions (3,3)\")\u001b[39m",
      "",
      "Stacktrace:",
      " [1] \u001b[1m_generic_matmatmul!\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,2}, ::Char, ::Char, ::Array{Int64,2}, ::Array{Int64,2}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:492\u001b[22m\u001b[22m",
      " [2] \u001b[1mgeneric_matmatmul!\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,2}, ::Char, ::Char, ::Array{Int64,2}, ::Array{Int64,2}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:483\u001b[22m\u001b[22m",
      " [3] \u001b[1m*\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,1}, ::Array{Int64,2}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:87\u001b[22m\u001b[22m",
      " [4] \u001b[1minclude_string\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::String, ::String\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./loading.jl:522\u001b[22m\u001b[22m",
      " [5] \u001b[1mexecute_request\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::ZMQ.Socket, ::IJulia.Msg\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/IJulia/src/execute_request.jl:193\u001b[22m\u001b[22m",
      " [6] \u001b[1m(::Compat.#inner#14{Array{Any,1},IJulia.#execute_request,Tuple{ZMQ.Socket,IJulia.Msg}})\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/Compat/src/Compat.jl:332\u001b[22m\u001b[22m",
      " [7] \u001b[1meventloop\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::ZMQ.Socket\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/IJulia/src/eventloop.jl:8\u001b[22m\u001b[22m",
      " [8] \u001b[1m(::IJulia.##13#16)\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./task.jl:335\u001b[22m\u001b[22m"
     ]
    }
   ],
   "source": [
    "A[1,:] * B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To get a row vector we must [transpose](https://en.wikipedia.org/wiki/Transpose) the slice `A[1,:]`.  In linear algebra, the transpose of a vector $x$ is usually denoted $x^T$.   In Julia, the transpose is `x.'`.\n",
    "\n",
    "If we omit the `.` and just write `x'` it is the [complex-conjugate of the transpose](https://en.wikipedia.org/wiki/Conjugate_transpose), sometimes called the *adjoint*, often denoted $x^H$ (in matrix textbooks), $x^*$ (in pure math), or $x^\\dagger$ (in physics).  For real-valued vectors (no complex numbers), the conjugate transpose is the same as the transpose, and correspondingly we usually just do `x'` for real vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1×3 RowVector{Int64,Array{Int64,1}}:\n",
       " 2  -1  5"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[1,:]'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, let's multiply this by $B$, which should give the first *row* of $C$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1×3 RowVector{Int64,Array{Int64,1}}:\n",
       " -14  5  10"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[1,:]' * B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " -14    5  10\n",
       "  -5  -20  10\n",
       "  -6   10   6"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "C"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Yup!\n",
    "\n",
    "Note that if we multiply a row vector by a matrix on the *left*, it doesn't really make sense.  Julia will give an error:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "ename": "LoadError",
     "evalue": "\u001b[91mDimensionMismatch(\"matrix A has dimensions (3,3), matrix B has dimensions (1,3)\")\u001b[39m",
     "output_type": "error",
     "traceback": [
      "\u001b[91mDimensionMismatch(\"matrix A has dimensions (3,3), matrix B has dimensions (1,3)\")\u001b[39m",
      "",
      "Stacktrace:",
      " [1] \u001b[1m_generic_matmatmul!\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,2}, ::Char, ::Char, ::Array{Int64,2}, ::Array{Int64,2}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:492\u001b[22m\u001b[22m",
      " [2] \u001b[1mgeneric_matmatmul!\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,2}, ::Char, ::Char, ::Array{Int64,2}, ::Array{Int64,2}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:483\u001b[22m\u001b[22m",
      " [3] \u001b[1mA_mul_Bc\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::Array{Int64,2}, ::Array{Int64,1}\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./linalg/matmul.jl:86\u001b[22m\u001b[22m",
      " [4] \u001b[1minclude_string\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::String, ::String\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./loading.jl:522\u001b[22m\u001b[22m",
      " [5] \u001b[1mexecute_request\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::ZMQ.Socket, ::IJulia.Msg\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/IJulia/src/execute_request.jl:193\u001b[22m\u001b[22m",
      " [6] \u001b[1m(::Compat.#inner#14{Array{Any,1},IJulia.#execute_request,Tuple{ZMQ.Socket,IJulia.Msg}})\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/Compat/src/Compat.jl:332\u001b[22m\u001b[22m",
      " [7] \u001b[1meventloop\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m::ZMQ.Socket\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m/Users/stevenj/.julia/v0.6/IJulia/src/eventloop.jl:8\u001b[22m\u001b[22m",
      " [8] \u001b[1m(::IJulia.##13#16)\u001b[22m\u001b[22m\u001b[1m(\u001b[22m\u001b[22m\u001b[1m)\u001b[22m\u001b[22m at \u001b[1m./task.jl:335\u001b[22m\u001b[22m"
     ]
    }
   ],
   "source": [
    "B * A[1,:]'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we multiply $B$ on the right by *all* the rows of $A$, we get $C$ again:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ A[1,:]'*B \n",
    "  A[2,:]'*B\n",
    "  A[3,:]'*B ] == C"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Equivalently, each row of $A$ specifies a linear combination of *rows* of $B$ to produce the rows of $C$.   So, **if you want to rearrange the *rows* of a matrix, multiply it by another matrix on the *left***.\n",
    "\n",
    "For example, let's do the transformation that *adds two times the first row of $B$ to the third row, and leaves the other rows untouched*.  This is resembles one of the steps of Gaussian elimination!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  1   0  -2\n",
       "  1  -5   1\n",
       " -3   0   3"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "B"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  1   0  -2\n",
       "  1  -5   1\n",
       " -1   0  -1"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ 1 0 0\n",
    "  0 1 0\n",
    "  2 0 1 ] * B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact, we can write *exactly* one of the steps for Gaussian elimination on $B$ in this form:\n",
    "\n",
    "Let's do the transformation that *adds three times the first row of $B$ to the third row, subtracts the first row from the second, and leaves the first row untouched*.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       " 1   0  -2\n",
       " 0  -5   3\n",
       " 0   0  -3"
      ]
     },
     "execution_count": 40,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "[ 1 0 0\n",
    " -1 1 0\n",
    "  3 0 1 ] * B"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As desired, this \"eliminates\" the sub-diagonal entries of the first column of B.   The nice thing about writing elimination algebraically in this way, instead of in words, is that it will allow us to *think* about the elimination process algebraically."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perspective 4: columns × rows\n",
    "\n",
    "The key to this perspective is to observe:\n",
    "\n",
    "* elements in column $i$ of $A$ only multiply elements in row $j$ of $B$\n",
    "* a column times a row vector, sometimes denoted $xy^T$, is an [outer product](https://en.wikipedia.org/wiki/Outer_product) and produces a \"rank-1\" *matrix*\n",
    "\n",
    "(See [this excellent paper by Gil Strang](https://doi.org/10.1080/00029890.2018.1408378) for more on this perspective applied to linear algebra. You will be in a better position to understand this at the end of 18.06, however.)\n",
    "\n",
    "For example, here is column 1 of $A$ times row 1 of $B$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3×3 Array{Int64,2}:\n",
       "  2  0  -4\n",
       "  3  0  -6\n",
       " -4  0   8"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[:,1] * B[1,:]'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we do this for all three rows and columns and add them up, we get $C$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "true"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A[:,1] * B[1,:]' + A[:,2] * B[2,:]' + A[:,3] * B[3,:]' == C"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So, from this perspective, we could write:\n",
    "\n",
    "$$\n",
    "AB = \\sum_{k=1}^3 (\\mbox{column } k \\mbox{ of } A) (\\mbox{row } k \\mbox{ of } B) = \\sum_{k=1}^3 A[:,k] \\, B[k,:]^T\n",
    "$$\n",
    "\n",
    "where in the last expression we have used Julia notation for slices."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perspective 5: submatrix blocks × blocks\n",
    "\n",
    "It turns out that all of the above are special cases of a more general rule, by which we can break up a matrix in to \"submatrix\" blocks and multiply the blocks.  Rows, columns, etc. are just blocks of different shapes.  We will do more of this later in the course."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Julia 0.6.2",
   "language": "julia",
   "name": "julia-0.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "0.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
